﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace gt.dotnetcore.webapi.extensions.ApiLogging
{
    /// <summary>
    /// 全局ApiLog日志记录中间件，记录http请求流程中的输入、输出
    /// </summary>
    public class GlobalApiLoggingMiddleware
    {
        private readonly ILogger _logger;
        private readonly RequestDelegate _next;


        public GlobalApiLoggingMiddleware(RequestDelegate next,
            ILogger<GlobalApiLoggingMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        //TODO:check 是否有内存泄漏
        public async Task InvokeAsync(HttpContext context)
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            var request = context.Request;
            request.EnableBuffering();
            //这里不要释放stream,否则后续读取request.body会报错
            var reader = new StreamReader(request.Body, Encoding.UTF8);
            var requestStr = await reader.ReadToEndAsync();
            //var requestStr = reader.ReadToEnd(); .net core 3.1 会报 Synchronous operations are disallowed. 错误
            request.Body.Position = 0;
            Stream originalBody = context.Response.Body;
            try
            {
                using (var memStream = new MemoryStream())
                {
                    context.Response.Body = memStream;

                    await _next.Invoke(context);

                    var log = new ApiLog()
                    {
                        ClientId = request.GetClientId(),
                        ClientIp = request.GetClientRealIp(),
                        HttpMethod = request.Method,
                        Request = requestStr,
                        RequestId = request.GetRequestId(),
                        RequestUrl = request.Path.Value,
                        QueryString = request.QueryString.Value,
                        ServerIp = request.Host.Value,
                        StatusCode = context.Response.StatusCode,
                        Duration = watch.ElapsedMilliseconds
                    };

                    memStream.Position = 0;
                    log.Response = await new StreamReader(memStream).ReadToEndAsync();

                    memStream.Position = 0;
                    await memStream.CopyToAsync(originalBody);

                    _logger.LogInformation(JsonConvert.SerializeObject(log));
                }
            }
            finally
            {
                //重新给response.body赋值，用于返回
                context.Response.Body = originalBody;
                watch.Stop();
            }

        }
    }
}
